package com.example.common.config.rabbit.order;

import com.example.common.config.rabbit.ware.WareTransactionalConfig;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * <p>
 *      下订单锁库存分布式事务 （柔性事务） MQ配置
 *      逻辑：交换机绑定两个队列（一个死信队列，一个正常队列），死信队列消息过期后发送到正常队列中去消费
 * </p>
 *
 * @author zyred
 * @since v 0.1
 **/
@Configuration
public class OrderTransactionalConfig {

    public static final String DELAY_QUEUE = "order.delay.queue";
    public static final String NORMAL_QUEUE = "order.release.queue";
    public static final String SEC_KILL_QUEUE = "order.sec.kill.order.queue";

    public static final String DELAY_KEY = "order.create.order";
    public static final String NORMAL_KEY = "order.release.order";
    public static final String SEC_KILL_KEY = "order.sec.kill.order";


    public static final String ORDER_EXCHANGE = "order-event-exchange";

    @Value("${order.queue.ttl}")
    public Integer ttl;

    /**
     * 创建死信队列
     *
     * @return  死信队列
     */
    @Bean(name = DELAY_QUEUE)
    public Queue orderDelayQueue () {
        return QueueBuilder.durable(DELAY_QUEUE)
                .withArgument("x-dead-letter-exchange", ORDER_EXCHANGE)
                .withArgument("x-dead-letter-routing-key", NORMAL_KEY)
                .withArgument("x-message-ttl", ttl)
                .build();
    }

    @Bean(name = SEC_KILL_QUEUE)
    public Queue secKillOrderQueue () {
        return QueueBuilder.durable(SEC_KILL_QUEUE).build();
    }

    /**
     * 订单正常队列
     *
     * @return  正常对垒
     */
    @Bean(name = NORMAL_QUEUE)
    public Queue orderReleaseQueue () {
        return QueueBuilder.durable(NORMAL_QUEUE).build();
    }

    /**
     * 库存正常队列
     *
     * @return  正常对垒
     */
    @Bean(name = WareTransactionalConfig.NORMAL_QUEUE)
    public Queue stockReleaseQueue () {
        return QueueBuilder.durable(WareTransactionalConfig.NORMAL_QUEUE).build();
    }

    /**
     * 创建交换机
     *
     * @return  top 交换机
     */
    @Bean(name = ORDER_EXCHANGE)
    public Exchange orderEventExchange () {
        return ExchangeBuilder.topicExchange(ORDER_EXCHANGE).build();
    }

    /**
     * 创建死信队列与交换机的绑定关系
     *
     * @param delay 死信队列
     * @param ex    交换机
     * @return      绑定关系
     */
    @Bean public Binding orderCreateOrderBinding (@Qualifier(DELAY_QUEUE) Queue delay,
                                            @Qualifier(ORDER_EXCHANGE) Exchange ex) {
        return BindingBuilder.bind(delay).to(ex).with(DELAY_KEY).noargs();
    }

    /**
     * 创建普通队列与交换机的绑定关系
     *
     * @param release   普通队列
     * @param ex        交换机
     * @return          绑定关系
     */
    @Bean public Binding orderReleaseOrderBinding ( @Qualifier(NORMAL_QUEUE) Queue release,
                                              @Qualifier(ORDER_EXCHANGE) Exchange ex) {
        return BindingBuilder.bind(release).to(ex).with(NORMAL_KEY).noargs();
    }

    /**
     * 订单交换机与库存正常队列绑定
     *
     *  当订单过期，会发送一个消息到 stock.release.queue 队列中，
     *  stock.release.queue 队列绑定了 order-event-exchange 交
     *  换机，库存服务只需要监听 stock.release.queue 队列中的消息
     *  就能拿到订单过期后推送给库存服务的消息
     *
     * @param stock     库存正常队列
     * @param ex        订单交换机
     * @return          绑定关系
     */
    @Bean public Binding orderReleaseOtherBinding ( @Qualifier(WareTransactionalConfig.NORMAL_QUEUE) Queue stock,
                                                    @Qualifier(ORDER_EXCHANGE) Exchange ex) {
        return BindingBuilder.bind(stock).to(ex).with(WareTransactionalConfig.ORDER_STOCK_BINDING_KEY).noargs();
    }

    /**
     * 秒杀服务订单创建队列绑定关系，主要是用来做高并发削峰
     *
     * @param secKill       秒杀队列
     * @param ex            订单交换机
     * @return              绑定关系
     */
    @Bean public Binding secKillOrderBinding (@Qualifier(SEC_KILL_QUEUE) Queue secKill,
                                               @Qualifier(ORDER_EXCHANGE) Exchange ex) {
        return BindingBuilder.bind(secKill).to(ex).with(SEC_KILL_KEY).noargs();
    }

}
